/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package swym.protocol.server;

import java.util.*;

import swym.server.config.*;

/**
 *
 * @author mschaefer
 */

import swym.protocol.InvalidRequestIDException;
import swym.protocol.*;
import swym.server.*;
import swym.server.file.*;

import java.io.*;
import java.net.*;

public class FileMessageHandler extends MessageHandler
{
    public static final String TYPE_STRING = "file";
       
    private FileAction fileAction;
    
    private final FileActionMessage message;
        
    public FileMessageHandler( ActionMessage message ) 
            throws InvalidMessageException, UnregisteredClientException
    {
        super( message );
        
        this.message = (FileActionMessage) message;
        
        FileAction.eFileActionType t = this.message.getFileActionType();
        
        fileAction = new FileAction( new SyncedFile( user, 
                                                     this.message.getPath(), 
                                                     this.message.getVersion() ),
                                     this.message.getFileActionType() );
    }
    
    @Override
    public void handleMessage( ClientSocket socket ) 
            throws IOException, MessageException
    {
        FileActionManager manager = user.getFileActionManager();
        
        // Handle message which denotes a client successfully
        // completed a file action
        if ( message.getFileActionType().equals( FileAction.eFileActionType.MARK ) )
        {
            markFileAction( message );
            writeConfirmMessage( socket );
        }
           
        else if ( message.getFileActionType().equals( FileAction.eFileActionType.GET ) )
            handleSendData( message, socket );
        
        else if ( message.getFileActionType().equals( FileAction.eFileActionType.STATUS ) )
            handleStatusRequest( message, socket );
        
        else 
        {
            // else, handle incoming data (unless it's a delete, in which case don't
            // do anything special)
            if ( !message.getFileActionType().equals( FileAction.eFileActionType.DELETE ) )
                handleNewData( socket );
        
            fileAction.setClientUpdated( client );
            manager.addFileAction( fileAction );
            
            writeConfirmMessage( socket );
        }
    }
    
    private void writeConfirmMessage( ClientSocket socket ) throws IOException
    {
        writeMessage( socket, new ConfirmActionMessage( user.getName(), 
                                                        client.getName(), 
                                                        requestID ) );
    }
    
    /**
     * Handles messages which signal that a client has an updated 
     * file action.
     * 
     * @param message
     */
    private void markFileAction( FileActionMessage message )
    {
        if ( isDebugMode )
            Log.writeEntry( "handling " + message.toString() );
        Vector<FileAction> actions = user.getFileActionManager().getFileActions( message.getPath() );
        for ( FileAction action : actions )
        {
            if ( action.getFile().getVersion().equals( message.getVersion() ) )
            {
                action.setClientUpdated( user.getClient( message.getClientName() ) );
            }
        }
    }
    
    private void handleStatusRequest( FileActionMessage message, ClientSocket socket )
            throws IOException
    {
        FileActionManager manager = user.getFileActionManager();
        
        FileAction action  = manager.getFileAction( message.getPath(), message.getVersion() );
        
        ConfirmActionMessage confirmation;
        confirmation = new ConfirmActionMessage( userName, clientName, requestID );
                
        if ( action != null )
        {
            HashMap<String, Boolean> clientTable = action.getClientTable();
            confirmation.setPayload( clientTable );
        }
        else
        {
            confirmation.setReturnCode( -1 );
            confirmation.setErrorMessage( "File " + message.getPath() + ":" + 
                                                    message.getVersion() + 
                                                    " does not exist on the server." ); 
        }
        
        writeMessage( socket, confirmation );
    }
    
    private void handleSendData( FileActionMessage message, ClientSocket socket )
            throws IOException
    {
        FileActionManager manager = user.getFileActionManager();
        FileAction action = manager.getFileAction( message.getPath(), message.getVersion() );
        
        FileActionMessage dataMessage = null;
        
        if ( action != null )
        {
            SyncedFile file = action.getFile();

            for ( Integer chunkOffset : file.getChunkOffsets() )
            {
                dataMessage = new FileActionMessage( file, 
                                                     client.getName(),  
                                                     FileAction.eFileActionType.CHUNK, 
                                                     1 );

                dataMessage.setPayload( file.getChunk( chunkOffset ) );

                writeMessage( socket, dataMessage );
            }
        }
        else
        {
            // send error message - file action doesn't exist on server
            
            ConfirmActionMessage errMessage;
            errMessage = new ConfirmActionMessage( user.getName(), 
                                                   client.getName(), 
                                                   requestID );
            errMessage.setReturnCode( -1 );
            errMessage.setErrorMessage( "File " + message.getPath() + ":" + 
                                        message.getVersion() + 
                                        " does not exist on the server." );  
            writeMessage( socket, errMessage );
        }
    }
    
    /**
     * Get all of the data associated with this message.
     * 
     * @param socket
     * @throws java.io.IOException
     * @throws swym.protocol.MessageException
     */
    private void handleNewData( ClientSocket socket ) throws IOException, MessageException 
    {
        int chunksCount = message.getChunkCount();
        
        try
        {
            // TODO: this must be fixed to actually take messages!
            for ( int i = 0; i < chunksCount; i++ )
            {
                FileActionMessage fileMessage = (FileActionMessage) readMessage( socket );

                FileChunk chunk = fileMessage.getFileChunk();

                if ( chunk == null )
                {
                    throw new InvalidMessageException( message.getRequestID(), 
                                                       "file action message missing data payload" );
                }
                
                if ( fileMessage.getRequestID() != message.getRequestID() )
                    throw new InvalidRequestIDException( message.getRequestID(), 
                                                         "expected request ID: " + requestID + 
                                                         " received " + fileMessage.getRequestID() );
                    
                fileAction.getFile().setChunk( chunk );  
                
                System.out.println( "recieved new file data from " + fileAction.getFile().toString() );
            }
        }
        catch ( ClassNotFoundException e )
        {
            throw new InvalidMessageException( message.getRequestID(), 
                                               e.getMessage() +
                                                " in FileMessageHandler.handleNewData()" );
        }
    }
}
